﻿using LightCAD.Core;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.Linq;
using System.Reflection;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace LightCAD.Runtime
{
    public class CommandCenter
    {

        public static  ConcurrentDictionary<Type, object> CmdEntryInstances = new ConcurrentDictionary<Type, object>();
        public static List<CommandCenter> Instances=new List<CommandCenter>();
        public static CommandCenter ActiveInstance = null;
        public List<string> Categories=new List<string>();
        //public  CommandCenter Instance { get; private set; } = new CommandCenter();
        public IDocumentEditor DocumentEditor { get; private set; }
        /// <summary>
        /// 静态的Command公共触发事件
        /// </summary>
        public static event CommandExecuteEventHandler CommonCommandExecute;
        public event CommandExecuteEventHandler CommandExecute;//add remove
        public ICommandControl Commander { get; private set; }
        /// <summary>
        /// =True 命令条进入活动模式，不再执行命令
        /// </summary>
        public bool InAction { get; set; }

        public List<LcCommand> Commands { get; } = new List<LcCommand>();

        public DocumentRuntime DocRt { get; private set; }

        private List<string> _cmdInfos = new List<string>();

        public bool IsInited { get; private set; }
        public CommandCenter(IDocumentEditor docEditor, ICommandControl commander)
        {
            this.DocRt = docEditor.DocRt;
            this.DocumentEditor = docEditor;
            this.Commander = commander;
            this.CommandExecute += CommandCenter_CommandExecute;
            this.CommandExecute += (obj , args )=>CommonCommandExecute?.Invoke(obj, args);
            //自动注册Document上的事件
            this.CommandExecute += this.DocRt.Action.Instance_CommandExecute;
            this.CommandExecute += this.DocRt.Instance_CommandExecute;

            lock (Instances) 
            {
                Instances.Add(this);
                if (ActiveInstance == null)
                    ActiveInstance = this;
            }
        }
        public void SetActive() 
        {
            ActiveInstance = this;
        }
        public void Initilize()
        {
            this.LoadInternals();
            this.LoadAssemblies();
            if (!this.IsInited)
                this.AttachEvents();
            this.Commander.SetAllCommonds(this.Commands.Select(C => C.Name).ToList());
            this.IsInited = true;
        }

        private void CommandCenter_CommandExecute(object sender, CommandExecuteEventArgs e)
        {
            if (e.Command.Name == "Cancel")
            {
                this.DocumentEditor.CancelAll();
            }
        }

        public void AttachEvents()
        {
            this.Commander.AttachInputEvent(this.OnInputKeyDown, this.OnInputKeyUp,this.OnInputGotFocus,this.OnInputLossFocus);
        }
        public void DetachEvents()
        {
            this.Commander.DetachInputEvent(this.OnInputKeyDown, this.OnInputKeyUp, this.OnInputGotFocus, this.OnInputLossFocus);
        }
        public void ClearInput()
        {
            this.Commander.SetInputText(string.Empty);
        }

        public void WriteCmdInfo(string info)
        {
            this.Commander.WriteInfo(info);
            _cmdInfos.Add(info);
        }
        public void WriteInfo(string info, bool clearInput = false)
        {
            this.Commander.WriteInfo(info);
            if (clearInput)
            {
                this.Commander.SetInputText(string.Empty);
            }
        }
        public List<string> GetCommandInfos()
        {
            var infos = new List<string>(this._cmdInfos);
            this._cmdInfos.Clear();
            return infos;
        }
        public void OnInputKeyDown(KeyEventRuntime e)
        {
            if (this.InAction) return;

            if (e.KeyCode == 13)//Enter
            {
                var str = this.Commander.GetInputText();
                ClearInput();
                var result = Execute(str);
                if (result.Success == true)
                {
                    this.Commander.AddHistoryCmd(str);
                    WriteInfo("命令:" + str);
                }
            }
        }
        public void OnInputKeyUp(KeyEventRuntime e)
        {
            if (this.InAction) return;


        }
        public void OnInputGotFocus(ICommandControl control, EventArgs args) 
        {
            //ActiveInstance = this;
        }
        public void OnInputLossFocus(ICommandControl control, EventArgs args) 
        {
        }
        public string ParseCommandString(string cmdString, out string[] args)
         {
            var parts = cmdString.Split(' ');
            args = new string[parts.Length - 1];
            if (args.Length > 0)
            {
                for (var i = 1; i < parts.Length; i++)                 //var可以引用任何数据类型，包括数字，字符串，对象，数组和函数
                {
                    args[i - 1] = parts[i];
                }
            }
            return parts[0];
        }

        /// <summary>
        /// 执行命令
        /// </summary>
        /// <param name="cmdString">命令(可带参数),例 "Circle 2P"</param>
        /// <returns></returns>
        public CommandResult Execute(string cmdString)
        {
            string[] args;
            var cmdName = ParseCommandString(cmdString, out args);
            var cmd = this.FindCommand(cmdName);
            if (cmd == null)
            {
                var cmdResult = RaiseCommand(new LcCommand { Name = cmdName, Args = args });
                if (cmdResult.Success == null)
                {
                    //弹出非法
                    // AppRuntim
                    this.WriteInfo("命令：" + cmdString);
                    this.WriteInfo("无效命令！");
                    this.Commander.SetInputText(string.Empty);
                }

                return cmdResult;
            }  

            if (cmd.Entry != null)
            {
                if (cmd.Entry.Instance == null)
                {
                    var instance= CmdEntryInstances.GetOrAdd(cmd.Entry.Type, (key) =>
                    {
                        return  Activator.CreateInstance(cmd.Entry.Type);
                    });
                    cmd.Entry.Instance = instance;
                }

                var result = (CommandResult)cmd.Entry.Method.Invoke(cmd.Entry.Instance, new object[] { DocumentEditor, args });
                if (result?.Success != null && result.Success.Value)
                {
                    //this.
                }
                return (CommandResult)result;
            }
            else
            {
                return RaiseCommand(cmd);
            }
        }

        /// <summary>
        /// 按命令名及快捷键进行搜索,并返回LcCommand
        /// #TODO:命令名及快捷键
        ///     1.未考虑重名冲突机制
        ///     2.使用Map或者Tree进行优化
        /// </summary>
        /// <param name="cmdToJudge">命令名或快捷键,不区分大小写</param>
        /// <returns>失败, 返回null</returns>
        private LcCommand FindCommand(string cmdToJudge)
        {
            if (Commands == null || Commands.Count == 0)
            {
                Debug.Assert(false);
                return null;
            }
              
            foreach (LcCommand cmd in Commands)
            {
                string lower = cmdToJudge.ToLower();
                if (lower.Equals(cmd.Name.ToLower()))
                {
                    return cmd;
                }
                foreach (string str in cmd.ShortCuts)
                {
                    if (lower.Equals(str.ToLower()))
                    {
                        return cmd;
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// 加载内置的命令，如save、open等
        /// </summary>
        private void LoadInternals()
        {
            //TODO
        }


        /// <summary>
        /// 加载所有程序集的命令的方法，如line、circle等
        /// </summary>
        private void LoadAssemblies()
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (Assembly asm in assemblies)
            {
                var asmName = asm.GetName().Name;
                Debug.Assert(asmName != null, nameof(asmName) + " != null");
              
                if (!LcRuntime.RegistAssemblies.Contains(asmName)) continue;

                var types = asm.GetTypes();
                foreach (Type type in types)
                {
                    var attr = type.GetCustomAttribute<CommandClassAttribute>();
                    if (attr == null) continue;

                    var methods = type.GetMethods();
                    foreach (var method in methods)
                    {
                        var mattr = method.GetCustomAttribute<CommandMethodAttribute>();
                        if (mattr == null) continue;
                        var category = mattr.Category;
                       
                        if (this.DocumentEditor.EditorType==EditorType.Model3d&&category!="Model")
                            continue;
                        if (this.DocumentEditor.EditorType == EditorType.Component && category != "Component")
                            continue;
                        if (this.DocumentEditor.EditorType == EditorType.Drawing)
                            if (!string.IsNullOrEmpty(category) && category != "Drawing")
                                continue;
                        var cmd = new LcCommand
                        {
                            Name = mattr.Name ?? method.Name,
                            Description = mattr.Descr,
                            ShortCuts = new List<string>(mattr.ShortCuts.Split(',')),
                            Category = mattr.Category,
                            Entry = new LcCommandEntry(asm, type, method)
                        };
                        Commands.Add(cmd);
                    }
                }
            }
        }


        private CommandResult RaiseCommand(LcCommand command)
        {
             if (this.CommandExecute != null)
            {
                var eventArgs = new CommandExecuteEventArgs
                {
                    Command = command,
                    Result = new CommandResult()
                };
                this.CommandExecute(this, eventArgs);
                return eventArgs.Result;
            }
            return null;
        }
    }
}